package com.jz.netthings.appengine.queue;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.jz.netthings.appengine.baseData.*;
import com.jz.netthings.appengine.mapper.JzMachineMapper;
import com.jz.netthings.appengine.support.BeanSupport;
import com.jz.netthings.appengine.support.SendMsgSupport;
import com.jz.netthings.exception.AdscmSystemException;
import com.jz.netthings.netty.protocol.bean.ZxdcMsgPacket;
import com.jz.netthings.netty.utils.NettyMsgHelper;
import com.jz.netthings.utils.AdscmBaseUtils;
import com.jz.netthings.utils.AdscmDateUtils;
import io.netty.channel.ChannelHandlerContext;
import org.springframework.data.redis.core.RedisTemplate;

import java.security.Provider;
import java.util.*;

/**
 * 设备管理
 */
public class MachineMgr {




    private static class SingletonHolder {

        private static MachineMgr	INSTANCE	= new MachineMgr();
    }

    public static MachineMgr getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * 记录分配机的通道上下文集合
     */
    private List<ChannelHandlerContext> allotClientCtxs	= new ArrayList<ChannelHandlerContext>();
    private List<MachineData> machineList	= new ArrayList<MachineData>();
    private HashMap<String, MachineData> machineMap	= new HashMap<String, MachineData>();
    private HashMap<String, Set<MachineData>>	nurseMachineMap	= new HashMap<String, Set<MachineData>>();
    private HashMap<String, ChannelHandlerContext> ctxMap	= new HashMap<String, ChannelHandlerContext>();
    private HashMap<ChannelHandlerContext,String> ctxMap_1	= new HashMap<ChannelHandlerContext,String>();

    public MachineMgr() {
    }

    /**
     * 重新初始化设备
     * @param jzMachineMapper
     * @param objectRedisTemplate
     */
    public void reInitMachine(JzMachineMapper jzMachineMapper, RedisTemplate<Object , Object> objectRedisTemplate){
        synchronized (this) {
            this.machineList.clear();
            this.machineMap.clear();
            nurseMachineMap.clear();
            ctxMap.clear();
            ctxMap_1.clear();
            this.allotClientCtxs.clear();

            Wrapper<JzMachine> wrapper = new EntityWrapper<>();
            wrapper.eq("state",1);

            List<JzMachine> machines = jzMachineMapper.selectList(wrapper);

            for (JzMachine machine : machines) {
                MachineData machineData = new MachineData();
                machineData.setCode(machine.getCode());
                machineData.setId(machine.getId()+"");
                machineData.setOnline(false);
                machineData.setNetwork(machine.getNetworking() == null ? false : machine.getNetworking().booleanValue());
                this.machineList.add(machineData);
                this.machineMap.put(machineData.getCode(), machineData);
                this.updateRedisMachineData(objectRedisTemplate,machineData);
            }
            Collections.sort(this.machineList);

        }
    }

    /**
     * 初始化设备
     * @throws AdscmSystemException
     */
    public void initialMachine(JzMachineMapper jzMachineMapper, RedisTemplate<Object , Object> objectRedisTemplate) {
        synchronized (this) {
            this.machineList.clear();
            this.machineMap.clear();
            nurseMachineMap.clear();
            ctxMap.clear();
            ctxMap_1.clear();
            this.allotClientCtxs.clear();

            Map<Object, Object> machineMap = objectRedisTemplate.opsForHash().entries("machineMap");
            if (machineMap != null && machineMap.size()>0){
                //证明缓存里面有东西
                Set<Object> keySet = machineMap.keySet();
                Iterator<Object> iterator = keySet.iterator();
                while(iterator.hasNext()){
                    String macNum = (String)iterator.next();
                    HashMap<String ,Object> hashMap = (HashMap) machineMap.get(macNum);
                    MachineData machine = new MachineData();
                    try{

                        Object id = hashMap.get("id");
                        if (id != null && !"".equals(id)){
                            machine.setId(hashMap.get("id")+"");
                        }
                        Object code = hashMap.get("code");
                        if (code != null && !"".equals(code)){
                            machine.setCode(hashMap.get("code")+"");
                        }
                        Object network = hashMap.get("network");
                        if (network != null && !"".equals(network)){
                            machine.setNetwork(new Boolean(hashMap.get("network")+""));
                        }
                        Object nurseCode = hashMap.get("nurseCode");
                        if (nurseCode != null && !"".equals(nurseCode)){
                            machine.setNurseCode(hashMap.get("nurseCode")+"");
                        }
                        Object nurseName = hashMap.get("nurseName");
                        if (nurseName != null && !"".equals(nurseName)){
                            machine.setNurseName(hashMap.get("nurseName")+"");
                        }
                        Object runStatus = hashMap.get("runStatus");
                        if (runStatus != null && !"".equals(runStatus)){
                            machine.setRunStatus(new Integer(hashMap.get("runStatus")+""));
                        }
                        Object statusInfo = hashMap.get("statusInfo");
                        if (statusInfo != null && !"".equals(statusInfo)){
                            machine.setStatusInfo(hashMap.get("statusInfo")+"");
                        }
                        Object weight = hashMap.get("weight");
                        if (weight != null && !"".equals(weight)){
                            machine.setWeight(new Integer(hashMap.get("weight")+""));
                        }
                        Object online = hashMap.get("online");
                        if (online != null && !"".equals(online)){
                            machine.setOnline(new Boolean(hashMap.get("online")+""));
                        }
                        Object allotTime = hashMap.get("allotTime");
                        if (allotTime != null && !"".equals(allotTime)){
                            machine.setAllotTime(AdscmDateUtils.parseDateL(hashMap.get("allotTime")+""));
                        }
                        Object collectingTime = hashMap.get("collectingTime");
                        if (collectingTime != null && !"".equals(collectingTime)){
                            machine.setCollectingTime(AdscmDateUtils.parseDateL(hashMap.get("collectingTime")+""));
                        }
                        Object waitTime = hashMap.get("waitTime");
                        if (waitTime != null && !"".equals(waitTime)){
                            machine.setWaitTime(new Long(hashMap.get("waitTime")+""));
                        }
                        Object suspend = hashMap.get("suspend");
                        if (suspend != null && !"".equals(suspend)){
                            machine.setSuspend(new Boolean(hashMap.get("suspend")+""));
                        }
                        Object gjzId = hashMap.get("gjzId");
                        if (gjzId != null && !"".equals(gjzId)){
                            machine.setGjzId(hashMap.get("gjzId")+"");
                        }

                    }catch (Exception e){
                        System.out.println("从缓存中初始化MachineData异常："+e.getMessage());
                    }
                    this.machineMap.put(macNum,machine);
                    this.machineList.add(machine);

                    String nurseCode = machine.getNurseCode();
                    if (nurseCode != null && !"".equals(nurseCode)){
                        //有护士编号
                        Set<MachineData> set = this.nurseMachineMap.get(nurseCode);
                        if (set == null) {
                            set = new HashSet<MachineData>();
                            this.nurseMachineMap.put(nurseCode, set);
                        }
                        set.add(machine);
                    }
                }

            }else{
                Wrapper<JzMachine> wrapper = new EntityWrapper<>();
                wrapper.eq("state",1);
                List<JzMachine> machines = jzMachineMapper.selectList(wrapper);

                for (JzMachine machine : machines) {
                    MachineData machineData = new MachineData();
                    machineData.setCode(machine.getCode());
                    machineData.setId(machine.getId()+"");
                    machineData.setOnline(false);
                    machineData.setNetwork(machine.getNetworking() == null ? false : machine.getNetworking().booleanValue());
                    this.machineList.add(machineData);
                    this.machineMap.put(machineData.getCode(), machineData);
                    this.updateRedisMachineData(objectRedisTemplate,machineData);
                }

            }


        }

        Collections.sort(this.machineList);
    }

    /**
     * 增加/更新Redis中存储的机器信息
     * @param objectRedisTemplate
     */
    public void updateRedisMachineData(RedisTemplate<Object , Object> objectRedisTemplate,MachineData machine){
        objectRedisTemplate.opsForHash().put("machineMap",machine.getCode(),machine);
    }

    /**
     * 绑定机器和通道上下文
     * @param macNum
     * @param ctx
     */
    public void bindCtxMac(String macNum,ChannelHandlerContext ctx){
        synchronized (this) {
//            ChannelHandlerContext context = ctxMap.get(macNum);
//            if (context != null){
//                System.out.println("移除原来绑定的机器和通道：【"+macNum+"】【"+context.channel().remoteAddress()+"】");
//                ctxMap_1.remove(context);
//                ctxMap.remove(macNum);
//                context.close();
//            }
            this.cancelBind(macNum);

            ctxMap.put(macNum,ctx);
            ctxMap_1.put(ctx,macNum);
        }
    }

    /**
     * 根据机器号，获取他的通道上下文
     * @param macNum
     */
    public ChannelHandlerContext getMacCtx(String macNum){
        synchronized (this) {
            return ctxMap.get(macNum);
        }
    }
    /**
     * 获取所有的分配通道上下文
     */
    public List<ChannelHandlerContext> getAllotCtxs(){
        return this.allotClientCtxs;
    }

    /**
     * 发送消息给分配机
     * @param packet
     */
    public void sendMsgToAllotCtxs(ZxdcMsgPacket packet){
        try {
            for (ChannelHandlerContext ctx :
                    this.allotClientCtxs) {
                ctx.channel().writeAndFlush(packet);
            }
        }catch (Exception e){
            System.out.println("发送消息给分配机异常："+e.getMessage());
        }
    }

    /**
     * 根据通道上下文，获取机器
     * @param ctx
     */
    public MachineData getMachine(ChannelHandlerContext ctx){
        synchronized (this) {
            String mac = ctxMap_1.get(ctx);

            if (mac != null && !"".equals(mac)){
                return machineMap.get(mac);
            }
            return null;
        }
    }
    /**
     * 根据机器号，获取机器对象
     * @param macNum
     */
    public MachineData getMachine(String macNum) {
        synchronized (this) {
            if (macNum != null && !"".equals(macNum)){
                return machineMap.get(macNum);
            }
            return null;
        }
    }

    public HashMap<String, Set<MachineData>> getNurseMachineMap() {
        return nurseMachineMap;
    }

    /**
     * 获取机器缓存队列
     * @return
     */
    public String getMachineQueue() {
        synchronized (this) {
            return JSON.toJSONString(machineList, SerializerFeature.PrettyFormat);
        }
    }


    /**
     * 手动分配采浆机
     * @param providerData
     * @param providerInfo
     * @return
     */
    public AllotData manualAllotByProvider(ProviderData providerData, ProviderInfo providerInfo,String mcode) throws AdscmSystemException{
        MachineData allotmachine = null;
        AllotData allotData = new AllotData();
         synchronized (this){
             allotmachine = this.getMachine(mcode);
             if (allotmachine == null) {
                 throw new AdscmSystemException("设备编号无效！");
             }

             int status = allotmachine.getStatus();
             if (status == MachineData.BUSY) {
                 throw new AdscmSystemException("当前设备状态不能进行分配，请等待一会后分配！");
             }

             if (status != MachineData.FREE) {
                 throw new AdscmSystemException("设备已经占用！");
             }
             allotmachine.doOccupy();
             //给采浆机器发送消息
             boolean result = sendMachineAllotInfo(providerData, providerInfo, mcode);
             //发送给平板
//             sendAllotInfoToChair(providerData, providerInfo, allotmachine.getCode());
             if (result){
                 allotData.setMachineNo(allotmachine.getId());
                 allotData.setProviderCode(providerData.getProviderNo());
                 allotData.setMachineCode(allotmachine.getCode());
                 allotData.setAllotTime(allotmachine.getAllotTime());
             }else {
                 throw new AdscmSystemException("采浆机未响应");
             }

         }

        return allotData;
    }

    public AllotData autoAllotByProvider(ProviderData providerData,ProviderInfo providerInfo)throws AdscmSystemException{
        MachineData allotmachine = null;
        AllotData allotData = new AllotData();
        synchronized (this){
            if (providerData.getCurrentStatus() >= 6) {
                //已分配
                allotmachine = machineMap.get(providerData.getMachineCode());
                if (allotmachine != null) {
                    allotData.setMachineNo(allotmachine.getId());
                    allotData.setProviderCode(providerData.getProviderNo());
                    allotData.setMachineCode(allotmachine.getCode());
                    allotData.setAllotTime(allotmachine.getAllotTime());
                    return allotData;
                }
            }
            long waittime = 0;
            for (MachineData machine : machineList) {
                if (machine.getStatus() == MachineData.FREE) {
                    //>= 4  <7  >=46
                    if (allotmachine == null) {
                        allotmachine = machine;
                        waittime = machine.getWaitTime();
                    } else {
                        if (machine.getWaitTime() < waittime) {
                            allotmachine = machine;
                            waittime = machine.getWaitTime();
                        }
                    }
                }
            }
            if (allotmachine == null) {
                throw new AdscmSystemException("没有空闲设备！");
            }
            //给采浆机器发送消息
            boolean result = sendMachineAllotInfo(providerData, providerInfo, allotmachine.getCode());
            //发送给平板
//            sendAllotInfoToChair(providerData, providerInfo, allotmachine.getCode());
            if (result){
                allotmachine.doOccupy();
                allotData.setMachineNo(allotmachine.getId());
                allotData.setProviderCode(providerData.getProviderNo());
                allotData.setMachineCode(allotmachine.getCode());
                allotData.setAllotTime(allotmachine.getAllotTime());

            }else {
                throw new AdscmSystemException("采浆机未响应");
            }
        }
        return allotData;
    }
    /**
     * 发送分配信息给采浆机
     * @return
     */
    private  boolean sendMachineAllotInfo(ProviderData provider, ProviderInfo providerInfo,String mcode){

        ChannelHandlerContext macCtx = MachineMgr.getInstance().getMacCtx(mcode);
        if (macCtx == null){
            return false;
        }
        MachineData machine = MachineMgr.getInstance().getMachine(mcode);
        ZxdcMsgPacket alloc_donor = new ZxdcMsgPacket();
        alloc_donor.setCmd("alloc_donor");
        alloc_donor.setTimeout(20000);
        alloc_donor.setHasResponse(true);
        alloc_donor.setLevel(1);
        HashMap<String, Object> values = new HashMap<String, Object>();
        values.put("regCode",provider.getRegisterNo());
        values.put("donor_no", provider.getProviderNo());
        values.put("donor_name", provider.getName());
        values.put("age", providerInfo.getAge());
        values.put("sex", provider.getSex());
        values.put("address",providerInfo.getAddress());
        values.put("photo", Base64.getEncoder().encodeToString(provider.getPhoto()));
        int pAge = 0;
        if (providerInfo.getProvideAge() != null){
            pAge = providerInfo.getProvideAge();
        }
        values.put("donating_age", pAge + "");//浆龄
        if (providerInfo.getLastDate() != null){
            values.put("last_donating_time", AdscmDateUtils.getNowOnlyDateFomat1(providerInfo.getLastDate()));//上一次献浆时间
        }

        values.put("complains_record", "");//投诉记录
        values.put("vessel_type", "");//血管类型
        values.put("times", "");//每年献浆次数
        values.put("mCode",machine.getCode());
        alloc_donor.setValues(values);

        List<HashMap<String, Object>> tList = new ArrayList<HashMap<String, Object>>();
        alloc_donor.setTaskList(tList);

        boolean result = NettyMsgHelper.sendAllotPacket(macCtx, alloc_donor);
        return result;
    }
    /**
     * 发送分配信息给平板
     * @return
     */
    private  boolean sendAllotInfoToChair(ProviderData provider, ProviderInfo providerInfo,String mcode){

        String chair = null;
        if (AdscmBaseUtils.getInstance().isNotEmpty(mcode,null)){
            //获取机器对应的平板
            chair = ChairMgr.getInstance().getChairByMac(mcode);
            if (!AdscmBaseUtils.getInstance().isNotEmpty(chair,null)){
                return false;
            }
        }else {
            return false;
        }

        ChannelHandlerContext chairCxt = ChairMgr.getInstance().getCtxByChair(chair);
        if (chairCxt == null){
            return false;
        }

        SendMsgSupport.auth_info(chairCxt,provider,providerInfo);

        return true;
    }

    /**
     * 发送取消分配浆员给采浆机
     * @param mcode
     * @param providerNo
     * @param regCode
     * @return
     */
    public boolean sendCancelDonor(String mcode,String providerNo,String regCode)throws AdscmSystemException{
        //发送给机器
        ChannelHandlerContext macCtx = this.getMacCtx(mcode);

        if (macCtx != null) {
            ZxdcMsgPacket packet = new ZxdcMsgPacket();
            packet.setCmd("cancel_donor");
            packet.setHasResponse(true);
            packet.setLevel(1);

            HashMap<String, Object> values = new HashMap<String, Object>();
            values.put("donorId", providerNo);
            values.put("regCode", regCode);
            packet.setValues(values);


            List<HashMap<String, Object>> list = new ArrayList<>();
            packet.setTaskList(list);

            boolean flag = NettyMsgHelper.sendCancelDonorPacket(macCtx,packet);
            if (flag){
                //如果成功了，再发送给平板
                ChannelHandlerContext chairCtx = ChairMgr.getInstance().getCtxByChairsMcode(mcode);
                ProviderData providerData = new ProviderData();
                providerData.setProviderNo(providerNo);
                SendMsgSupport.cancel_donor(chairCtx,providerData);
            }
            return flag;
        }
        throw new AdscmSystemException("取消分配浆员异常，没有获取到浆机的长连接引擎，请稍后再试...");
    }

    /**
     * 设置设备为空闲设备
     * @param mcode
     * @throws AdscmSystemException
     */
    public void doFreeMachine(String mcode)throws AdscmSystemException{

        synchronized (this){
            MachineData data = this.machineMap.get(mcode);
            if (data == null) {
                throw new AdscmSystemException("设备无效！");
            }
            data.doFree();


            ZxdcMsgPacket allotMsg = new ZxdcMsgPacket();
            allotMsg.setCmd("machine_statusInfo");
            allotMsg.setHasResponse(false);
            allotMsg.setLevel(3);
            HashMap<String, Object> values = new HashMap<String, Object>();
            values.put("mcode", data.getCode());
            values.put("info", JSON.toJSONString(data, SerializerFeature.PrettyFormat));
            allotMsg.setValues(values);
            MachineMgr.getInstance().sendMsgToAllotCtxs(allotMsg);


        }
    }

    /**
     * 解绑机器和通道
     * @param mcode
     */
    private void cancelBind(String mcode){
        ChannelHandlerContext context = this.ctxMap.get(mcode);
        if (context != null){
            System.out.println("移除原来绑定的机器和通道：【"+mcode+"】【"+context.channel().remoteAddress()+"】");
            ctxMap_1.remove(context);
            ctxMap.remove(mcode);
            context.close();
        }
    }

}
